#! /bin/bash

if [ -n "$1" ]
then
TESTNAME="$1"
else
echo -n "Введите имя папки:"
read TESTNAME
fi

AUTHOR="Сергей Балакший, t.me/sergeBN, @SergeBN"
WIDTH="1" # Ширина регистра, данных на входе и выходе для модуля $TESTNAME
CURDIR="$PWD"
FULLDIRNAME="$CURDIR/$TESTNAME"
echo "cur_dir: $CURDIR"
echo "dirname: $TESTNAME"
echo "test name $FULLDIRNAME"

echo "Создаём проект" $TESTNAME
mkdir -p $FULLDIRNAME/bench/cpp $FULLDIRNAME/bench/formal $FULLDIRNAME/sim $FULLDIRNAME/rtl

touch $FULLDIRNAME/Makefile $FULLDIRNAME/bench/formal/Makefile $FULLDIRNAME/bench/cpp/Makefile $FULLDIRNAME/rtl/Makefile $FULLDIRNAME/sim/Makefile

touch $FULLDIRNAME/README.org $FULLDIRNAME/bench/formal/$TESTNAME.sby $FULLDIRNAME/bench/cpp/main.cpp $FULLDIRNAME/rtl/$TESTNAME.sv $FULLDIRNAME/sim/$TESTNAME\_tb.sv
rm -f Makefile
cat >> Makefile<<EOF
all: rtl bench test
SUBMAKE := \$(MAKE) --no-print-directory -C
TESTNAME=:TESTNAME:
.PHONY: rtl
rtl:
	\$(SUBMAKE) rtl

.PHONY: bench
bench: rtl
	\$(SUBMAKE) bench/cpp

.PHONY: test
test: bench
	\$(SUBMAKE) bench/cpp test

.PHONY: vtb itb
vtb: 
	\$(SUBMAKE) sim vtb

itb: 
	\$(SUBMAKE) sim itb

.PHONY: dot
dot:
	\$(SUBMAKE) sim dot

.PHONY: exe
exe:
	\$(SUBMAKE) sim exe

.PHONY: exe_b
exe_b:
	\$(SUBMAKE) sim exe_b

.PHONY: exe_t
exe_t:
	\$(SUBMAKE) sim exe_t

#sim/\$(TESTNAME)\_tb.sv

#sim/V\$(TESTNAME)\_tb.sv:
#	$(SUBMAKE) sim exe


.PHONY: clean
clean:
	\$(SUBMAKE) sim		clean
	\$(SUBMAKE) rtl		clean
	\$(SUBMAKE) bench/cpp	clean
	\$(SUBMAKE) doc		clean

EOF
echo "Обрабатываем корневой Makefile:"
gawk -v nametest=$TESTNAME '{ tmp=gensub(/:TESTNAME:/, nametest, 1); tmp=gensub(/:TESTNAME:/,nametest,1,tmp); print tmp }' < Makefile > $TESTNAME/Makefile

echo "Создаём README"

cat >> README.org <<EOF
Test:
Start:
Author:
EOF

### Почему-то точки (.) в значении переменной author воспринимает как что-то
### другое. И выдаёт ошибку
### Пока от такого запуска откажусь
###gawk -v name=$TESTNAME -v filename=README.org '/start:/  {print $0 " " strftime() name, filename}' README.org > $TESTNAME/README.org
#     -v author=$AUTHOR  \
##gawk -v testname=$TESTNAME \
##     -v author="Serge BN" \
##     -v README_FILE=README.org \
##' BEGIN { 
##     if ((getline tmp < README_FILE) > 0) {
##        print tmp " " testname
##	getline tmp < README_FILE
##	print tmp " " strftime()
##	getline tmp < README_FILE
##	print tmp " " author
##     }else{
##        err = README_FILE " - cannot open"
##        print err > "/dev/stderr"
##        exit 1
##     } 
##}' README.org > $TESTNAME/README.org

echo "Скрипт awk, который делает тоде самое. Обрати внимание. Скрипт и файл со скриптом оформляются немного по разному. Оставил его для сравнения"
cat >test_readme_v1.awk <<EOF
# test_readme_v1.awk -- create fields in README.org
# Serge Balakshiy, t.me/sergeBN, @SergeBN
# Apr 2024
# input is: testname - this is a project test name
# input is: README_FILE - this is a README.org
# output is: testname/README.org
BEGIN {
  if ((getline tmp_str < README_FILE) > 0){
    print tmp_str " " testname
    getline tmp_str < README_FILE
    print tmp_str " " strftime()
    getline tmp_str < README_FILE
    print tmp_str " " author
  }else{
    err = README_FILE " - cannot open"
    print err > "/dev/stderr"
    exit 1
  }
}
EOF
##echo "Запускаем скрипт"
##awk -v testname=$TESTNAME -v README_FILE=README.org -f test_readme_v1.awk > $TESTNAME/README.org

##cat $TESTNAME/README.org
##awk -v testname=$TESTNAME -v author="Serge B.N." -v README_FILE="README.org" -f test_readme_v1.awk > $TESTNAME/README.org

cat >test_readme_v2.awk <<EOF
# test_readme_v2.awk -- create fields in README.org
# Serge Balakshiy, t.me/sergeBN, @SergeBN
# Apr 2024
# vsriable is: tname - имя проекта для нового теста
# vsriable is: author - имя автора
# input is: README_FILE - файл README.org в корневой директории
# output is: testname/README.org для проекта
{ tmp=gensub(/Test:/,"Тест: " tname,1)
  tmp=gensub(/Start:/,"Старт: " strftime(),1,tmp)
  tmp=gensub(/Author:/,"Автор: " author,1,tmp)
  print tmp
}
EOF
echo "Обрабатываем README.org"
cat $TESTNAME/README.org
awk -v tname="$TESTNAME" -v author="$AUTHOR" -f test_readme_v2.awk < README.org > $TESTNAME/README.org

cat $TESTNAME/README.org

echo "Создаём timescale"
cat > $TESTNAME/rtl/timescale.v <<EOF
\`timescale 1ns / 10ps
EOF

###############################################################
### Директория rtl
###############################################################

echo "Создаём шаблонный модуль RTL с заданным именем: " $TESTNAME
cat > $TESTNAME.sv <<EOF
//\`include timescale.v
module :NAME: #(parameter WD=:WIDTH:) 
  (
  input  wire              clk,
  input  wire              rstn,
  input  wire [WD-1:0] d,
  output wire [WD-1:0] q
  );
  reg [WD-1:0] rq;
  assign q = rq;

  always @(posedge clk) begin
    if(!rstn)
      rq <= 0;
    else
      rq <= d;
  end

\`ifdef FORMAL
   reg f_past_valid;

   initial begin
      f_past_valid = 1'b0;
      st_cnt  = 4'b0;
   end
   always @(posedge clk)
     f_past_valid <= 1'b1;
   always @(*)
     if(!f_past_valid) begin
        assume((!rstn & rst) | al);
     end

\`endif

endmodule
EOF
echo "Обрабатываем основной модуль " $TESTNAME.sv
gawk -v mname="$TESTNAME" -v wd=$WIDTH '{ tmp=gensub(/:NAME:/, mname, 1); tmp=gensub(/:WIDTH:/,wd,1,tmp); print tmp }' < $TESTNAME.sv > $TESTNAME/rtl/$TESTNAME.sv

cat $TESTNAME/rtl/$TESTNAME.sv
###############################################################
### Директория sim
###############################################################
cat  > $TESTNAME\_tb.sv <<EOF
// Это можно собрать как
// \$ iverilog -I ../rtl -I . ../rtl/t3.sv t3_tb.sv
// \$ vvp a.out
// \$ vvp a.out
// Так и
// \$ verilator --binary --build --trace  --timing -top t3_tb ../rtl/t3.sv t3_tb.sv
// \$ gtkwave out.vcd
// Работает и так и эдак. Теперь можно написать Makefile

//\`timescale 1ns / 10ps
module :NAMETB: ();
  localparam WD=:SIZE:;
  reg clk, rstn;
  reg  [WD-1:0] d;
  wire [WD-1:0] q;

  initial begin
    #0 clk = 1;
    //forever #10 clk = ~clk;
  end
   always #10 clk <= ~clk;
   
  :NAMEDUT: #(.WD(WD)) inst(.clk(clk), .rstn(rstn), .d(d), .q(q));
   reg [32-1:0] seed;
   
  initial begin
    \$dumpfile("out.vcd");
    \$dumpvars();
     
    rstn = 1;
    d    = 0;
    #20  rstn = 0;
    #20  rstn = 1;
    repeat (48) begin
      #30 d = ~d;
    end
    \$finish();
  end
endmodule

EOF

echo "Обрабатываем модуль стенда:" $TESTNAME\_tb.sv
gawk -v nametb=$TESTNAME\_tb -v wd=1 -v namedut=$TESTNAME '{ tmp=gensub(/:NAMETB:/, nametb, 1); tmp=gensub(/:SIZE:/,wd,1,tmp);  tmp=gensub(/:NAMEDUT:/,namedut,1,tmp); print tmp }' < $TESTNAME\_tb.sv > $TESTNAME/sim/$TESTNAME\_tb.sv

cat $TESTNAME/sim/$TESTNAME\_tb.sv

cat > $TESTNAME.ys <<EOF
echo on

hierarchy -top :TESTNAME:
select -module :TESTNAME:
select -list
select t:*
select -list
select -set new_cells %
select -clear
show -format dot -prefix :TESTNAME:_show :TESTNAME:
show -format dot -prefix new_cells_show -notitle @new_cells
show -color maroon3 @new_cells -color cornflowerblue p:* -notitle -format dot -prefix :TESTNAME:_hier

# ========================================================

proc -noopt
select -set new_cells t: t:*dff
show -color maroon3 @new_cells -notitle -format dot -prefix :TESTNAME:_proc

# ========================================================

opt_expr; clean
select -set new_cells t:
show -color cornflowerblue @new_cells -notitle -format dot -prefix :TESTNAME:_clean

# ========================================================

EOF
echo "Обрабатываем скрипт sim/$TESTNAME.ys"

gawk -v testname=$TESTNAME '{ tmp=gensub(/:TESTNAME:/, testname, 1); tmp=gensub(/:TESTNAME:/,testname,1,tmp); print tmp }' < $TESTNAME.ys > $TESTNAME/sim/$TESTNAME.ys

echo "Получили скритт sim/$TESTNAME.ys"
cat $TESTNAME/sim/$TESTNAME.ys
echo
echo "Получаем sim/Makefile"
rm Makefile
cat > Makefile<<EOF
.PHONY: vtb itb

TESTNAME = :TESTNAME:
MTOP = \$(TESTNAME)_tb
SRC = ../rtl/\$(TESTNAME).sv \$(TESTNAME)_tb.sv

#\$(TESTNAME)_tb.sv

VFLAGS = --binary -Wall --trace  --timing
VTOP = -top \$(MTOP)

IFLAGS = -I ../rtl -I .

vtb: vsim vvcd wave

vsim: \$(SRC)
	verilator \$(VFLAGS) \$(VTOP) \$^ 
vvcd:obj_dir/V\$(TESTNAME)_tb
	\$^
wave: out.vcd
	gtkwave out.vcd


itb: isim ivcd wave

isim: \$(SRC)
	iverilog \$(IFLAGS) \$^

ivcd: a.out
	vvp \$^

.PHONY: dot

dot:\$(TESTNAME).ys
	yosys -s \$^ ../rtl/\$(TESTNAME).sv

.PHONY: exe

exe: del_dir post_exe

del_dir:
	rm -f -r obj_dir

CSRC=../rtl/\$(TESTNAME).sv ../bench/cpp/sim_main.cpp 
post_exe: obj_dir/V\$(TESTNAME)
obj_dir/V\$(TESTNAME): \$(CSRC)
	verilator --cc --exe --build -j 0 -Wall \$^

.PHONY: exe_a

exe_a: del_dir_a post_exe_a

del_dir_a:
	rm -f -r obj_dir

CPPSRC=../rtl/\$(TESTNAME).sv ../bench/cpp/sim1_main.cpp 
post_exe_a: obj_dir/V\$(TESTNAME).cpp
obj_dir/V\$(TESTNAME).cpp: \$(CPPSRC)
	verilator --cc --exe --build --timing -j 0 -Wall \$^

.PHONY: exe_b
exe_b: rm_obj post_exe_b build_exe run_exe_b

rm_obj:
	rm -f -r obj_dir

post_exe_b: \$(CPPSRC)
	verilator --cc --exe --trace --timing -Wall \$^

build_exe:
	make -j -C obj_dir -f V\$(TESTNAME).mk V\$(TESTNAME)

run_exe_b:
	echo "\n\$(TESTNAME) is runnig. To interrupt do Ctl+C\n"
	obj_dir/V\$(TESTNAME)

## exe_t
CPPSRCT=../rtl/\$(TESTNAME).sv ../bench/cpp/simt_main.cpp 

.PHONY: exe_t
exe_t: rm_obj post_exe_t buildt_exe run_exe_b

#rm_obj:
#	rm -f -r obj_dir

post_exe_t: \$(CPPSRCT)
	verilator --cc --exe --trace --timing -Wall \$^

buildt_exe:
	make -j -C obj_dir -f V\$(TESTNAME).mk V\$(TESTNAME)

run_exe_t:
	echo "\n\$(TESTNAME) is runnig. To interrupt do Ctl+C\n"
	obj_dir/V\$(TESTNAME)

.PHONY: clean

clean: 
	rm out.vcd
	rm a.out 
	rm -r obj_dir
EOF

echo "Обрабатываем sim/Makefile:"
gawk -v nametest=$TESTNAME '{ tmp=gensub(/:TESTNAME:/, nametest, 1); tmp=gensub(/:SIZE:/,wd,1,tmp);  tmp=gensub(/:NAMEDUT:/,namedut,1,tmp); print tmp }' < Makefile > $TESTNAME/sim/Makefile
echo "###############################################################"
echo "### Секция bench/formal"
echo "###############################################################"

echo "###############################################################"
echo "### Секция bench/cpp"
echo "###############################################################"
echo "Здесь я буду реализовывать простую схему управления. Модкль main.cpp и вспомогательный класс testh.h"

echo "==============================================================="
echo "### Makefile"
echo "==============================================================="

echo "==============================================================="
echo "### test.h"
echo "==============================================================="

echo "==============================================================="
echo "### main.cpp"
echo "==============================================================="
cat >sim_main.cpp <<'EOF'
  #include "V:TESTNAME:.h"
  #include "verilated.h"
  int main(int argc, char** argv) {
      VerilatedContext* contextp = new VerilatedContext;
      contextp->commandArgs(argc, argv);
      V:TESTNAME:* top = new V:TESTNAME:{contextp};

      while (!contextp->gotFinish()) { top->eval(); }
      delete top;
      delete contextp;
      return 0;
  }
EOF
echo "Обрабатываем bench/cpp/main.cpp:"
gawk -v nametest=$TESTNAME '{ tmp=gensub(/:TESTNAME:/, nametest, 1); tmp=gensub(/:TESTNAME:/,nametest,1,tmp); print tmp }' < sim_main.cpp > $TESTNAME/bench/cpp/sim_main.cpp
cat >sim1_main.cpp <<'EOF'
  #include "V:TESTNAME:.h"
  #include "verilated.h"
  #include "verilated_vcd_c.h"

  int main(int argc, char** argv) {
    Verilated::mkdir("logs");

    const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
    contextp->debug(0);
    contextp->commandArgs(argc, argv);
    contextp->randReset(2);
    contextp->traceEverOn(true);

    VerilatedVcdC* tfp = new VerilatedVcdC;
    if(tfp==0){printf("Error tfp\n"); return 1;}
    const std::unique_ptr<V:TESTNAME:> top{new V:TESTNAME: {contextp.get(), "TOP"}};

    top->trace(tfp, 99);  // Trace 99 levels of hierarchy (or see below)
    //tfp->dumpvars(1, "t");  // trace 1 level under "t"
    tfp->open("out.vcd");

    top->rstn = !0;
    top->clk = 0;
    uint16_t cnt=0;
    while (!contextp->gotFinish()) { 
      contextp->timeInc(1);  // 1 timeprecision period passes...
      top->clk = !top->clk;
      if (!top->clk) {
        if (contextp->time() > 1 && contextp->time() < 10) {
          top->rstn = !1;  // Assert reset
        } else {
          top->rstn = !0;  // Deassert reset
        }
        // Assign some other inputs
        //top->in_quad += 0x12;
        if (cnt < 32){
          cnt++;
        }else{
          cnt = 0;
          top->d = !top->d;
        }
      }
      top->eval();
      tfp->dump(contextp->time());
    }
    top->final();
    tfp->close();
    //delete top;
    //delete contextp;
    return 0;

    // Coverage analysis (calling write only after the test is known to pass)
#if VM_COVERAGE
    Verilated::mkdir("logs");
    contextp->coveragep()->write("logs/coverage.dat");
#endif

    // Final simulation summary
    // contextp->statsPrintSummary();
 
 }
EOF
echo "Обрабатываем bench/cpp/main.cpp:"
gawk -v nametest=$TESTNAME '{ tmp=gensub(/:TESTNAME:/, nametest, 1); tmp=gensub(/:TESTNAME:/,nametest,1,tmp); print tmp }' < sim1_main.cpp > $TESTNAME/bench/cpp/sim1_main.cpp

echo "==============================================================="
echo "### Цель exe_t. t_main.cpp"
echo "==============================================================="

cat > simt_main.cpp <<EOF
  #include "V:TESTNAME:.h"
  #include "verilated.h"
  #include "testb.h"

  int main(int argc, char** argv) {
    VerilatedContext *contextp = new VerilatedContext;
    contextp->commandArgs(argc, argv);
    contextp->debug(0);
    contextp->randReset(2);
    contextp->traceEverOn(true);

    TESTB<V:TESTNAME:> *tb = new TESTB<V:TESTNAME:>;
      
    tb->opentrace("out.vcd");

    while (!contextp->gotFinish()) { tb->tick(); }

    tb->closetrace();
    delete tb;
    delete contextp;

    return 0;
  }

EOF

echo "Обрабатываем bench/cpp/simt_main.cpp:"
gawk -v nametest=$TESTNAME '{ tmp=gensub(/:TESTNAME:/, nametest, 1); tmp=gensub(/:TESTNAME:/,nametest,1,tmp); print tmp }' < simt_main.cpp > $TESTNAME/bench/cpp/simt_main.cpp

rm simt_main.cpp

cat > $TESTNAME/bench/cpp/testb.h <<EOF
#ifndef TESTB_H
#define TESTB_H

#include <stdio.h>
#include <stdint.h>
#include "verilated.h"
#include <verilated_vcd_c.h>

#define TBASSERT(TB,A) do { if (!(A)) { (TB).closetrace(); } assert(A); } while(0);

template <class VA>     class TESTB {
public:
  VA              *m_core;
  VerilatedVcdC*  m_trace;
  uint64_t        m_tickcount;

  TESTB(void) : m_trace(NULL), m_tickcount(0l) {
    m_core = new VA;
    m_core->clk = 0;
    m_core->rstn = !0;
    eval(); // Get our initial values set properly.
  }

  virtual ~TESTB(void) {
    closetrace();
    delete m_core;
    m_core = NULL;
  }
         
  virtual void opentrace(const char *vcdname) {
    if (!m_trace) {
      m_trace = new VerilatedVcdC;
      m_core->trace(m_trace, 99);
      m_trace->open(vcdname);
    }
  }
                
  virtual void closetrace(void) {
    if (m_trace) {
      m_trace->close();
      delete m_trace;
      m_trace = NULL;
    }
  }
  virtual void eval(void) {
    m_core->eval();
  }

  virtual void tick(void) {
    m_tickcount++;

  // Убедитесь, что мы получили наши оценки непосредственно перед началом
  // работы. Это необходимо, так как в некоторых модулях подключения могли
  // быть внесены изменения, от которых зависит некоторая логика. 
  // Это заставляет эту логику пересчитываться до начала тактового сигнала.
  // Предполагается, что в тестируемом модуле есть сигнал clk.
  // Линия с сигналом d является информационным входом в модуль.
   eval();
  // tick масштабируем на 10
   if (m_trace) m_trace->dump((vluint64_t)(10*m_tickcount-2));
     m_core->clk = 1; // Управляем линией clk
     eval();
     if (m_trace) m_trace->dump((vluint64_t)(10*m_tickcount));
     m_core->clk = 0; // Управляем линией clk
     eval();
     if (m_trace) {
       m_trace->dump((vluint64_t)(10*m_tickcount+5));
       m_trace->flush();
     }
     if((10*m_tickcount)>10 && (10*m_tickcount)< 100){
       m_core->rstn = !1; // Assert reset
     }else {
       m_core->rstn = !0; // Deassert reset
     }
     // Управляем входом d
     if( (10*m_tickcount)%13==0){m_core->d = !m_core->d;}
  }
                
  unsigned long   tickcount(void) {
    return m_tickcount;
  }
};
        
#endif
EOF


###############################################################
### Удаляем лишнее, что понаделали
###############################################################
rm -f Makefile
rm -f README.org
rm -f *.sv
rm -f *.ys
rm -f test_readme_v1.awk
rm -f test_readme_v2.awk
rm -f sim_main.cpp
rm -f sim1_main.cpp
#gawk -v testname=$TESTNAME -v TEST_FILE=$TESTNAME.sv ' BEGIN {
